package com.pan.insist.controller.admin;

import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.pan.insist.annotation.CacheLock;
import com.pan.insist.annotation.OperateLog;
import com.pan.insist.base.BaseController;
import com.pan.insist.bean.LayuiTreeNode;
import com.pan.insist.constant.CommonConstant;
import com.pan.insist.constant.OperateConstant;
import com.pan.insist.exception.JsonException;
import com.pan.insist.exception.ViewException;
import com.pan.insist.model.MenuModel;
import com.pan.insist.service.MenuService;
import com.pan.insist.service.RoleService;
import com.pan.insist.util.RegexUtil;
import lombok.extern.log4j.Log4j2;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.*;

/**
* <p>
* 菜单管理-前端控制器
* </p>
*
* @author kaiji
* @since 2019-08-17
*/
@Log4j2
@Controller
@RequestMapping("/menu")
public class MenuController extends BaseController {

    private static final String PAGE_PREFIX = "menu/";
    @Resource
    private MenuService menuService;
    @Resource
    private RoleService roleService;

    @GetMapping("list")
    public String toList() {
        return PAGE_PREFIX + LIST;
    }

    @ResponseBody
    @GetMapping("listJson")
    @OperateLog(operateModule = "菜单管理", operateType = OperateConstant.SELECT, operateDesc = "查询菜单列表")
    public JSONObject list(Page<MenuModel> iPage, MenuModel menuModel) {
        iPage.setSize(Long.MAX_VALUE);
        QueryWrapper<MenuModel> queryWrapper = new QueryWrapper<>();
        if (StringUtils.isNotBlank(menuModel.getName())) {
            queryWrapper.lambda().like(MenuModel::getName, menuModel.getName().trim());
        }
        if (StringUtils.isNotBlank(menuModel.getUrl())) {
            queryWrapper.lambda().like(MenuModel::getUrl, menuModel.getUrl().trim());
        }
        iPage = menuService.page(iPage, queryWrapper);
        iPage.getRecords().forEach(this::assignmentForPidName);
        return page2LayTablePage(iPage);
    }

    @GetMapping("/view")
    @CacheLock(prefix = "view")
    @OperateLog(operateModule = "菜单管理", operateType = OperateConstant.VIEW, operateDesc = "查看菜单详细信息")
    public String view(String id, ModelMap modelMap) {
        if (StringUtils.isBlank(id)) {
            throw new RuntimeException("id不存在");
        }
        MenuModel menuModel = menuService.getById(id);
        modelMap.put("menuModel", menuModel);
        return PAGE_PREFIX + VIEW;
    }

    @GetMapping("update")
    @OperateLog(operateModule = "菜单管理", operateType = OperateConstant.VIEW, operateDesc = "跳转到修改页面")
    public String toUpdate(String id, ModelMap modelMap) {
        if (StringUtils.isBlank(id)) {
            throw new ViewException("请选择一条数据！");
        }

        MenuModel menuModel = menuService.getById(id);
        if (menuModel == null) {
            log.info("查询不到数据！");
            throw new ViewException("请选择一条数据！");
        }
        assignmentForPidName(menuModel);
        // 获取上级菜单
        List<MenuModel> menuModelList =  menuService.getTopMenu();

        modelMap.put("menuModel", menuModel);
        modelMap.put("menuModelList", menuModelList);
        return PAGE_PREFIX + INPUT;
    }

    private void assignmentForPidName(MenuModel menuModel) {
        MenuModel menu = menuService.getById(menuModel.getPid());
        String pidName = Optional.ofNullable(menu)
                .filter(m -> StringUtils.isNotBlank(m.getName()))
                .orElse(new MenuModel().setName("顶级父类")).getName();
        menuModel.setPidName(pidName);
    }

    @ResponseBody
    @PostMapping("/update")
    @CacheLock(prefix = "menu-update")
    @OperateLog(operateModule = "菜单管理", operateType = OperateConstant.UPDATE, operateDesc = "修改菜单信息")
    public JSONObject update(MenuModel menuModel) {
        // 检测验证数据
        JSONObject jsonObject = validate(menuModel, CommonConstant.DATA_UPDATE_TYPE);
        Integer code = jsonObject.getIntValue(CommonConstant.AjaxCode.CODE);
        if (CommonConstant.AjaxCode.FAIL_CODE.equals(code)) {
            return jsonObject;
        }

        jsonObject = new JSONObject();
        menuModel.setUpdateDate(LocalDateTime.now());
        menuService.updateById(menuModel);
        jsonObject.put(CommonConstant.AjaxCode.MESSAGE, "修改菜单数据成功！");
        jsonObject.put(CommonConstant.AjaxCode.CODE, CommonConstant.AjaxCode.SUCCESS_CODE);
        return jsonObject;

    }

    @GetMapping("save")
    @OperateLog(operateModule = "菜单管理", operateType = OperateConstant.VIEW, operateDesc = "跳转到保存页面")
    public String toSave(ModelMap modelMap) {
        // 获取上级菜单
        List<MenuModel> menuModelList =  menuService.getTopMenu();

        modelMap.put("menuModelList", menuModelList);
        return PAGE_PREFIX + INPUT;
    }

    @ResponseBody
    @PostMapping("save")
    @CacheLock(prefix = "menu-save")
    @OperateLog(operateModule = "菜单管理", operateType = OperateConstant.SAVE, operateDesc = "保存菜单信息")
    public JSONObject save(MenuModel menuModel) {
        JSONObject jsonObject = validate(menuModel, CommonConstant.DATA_SAVE_TYPE);
        Integer code = jsonObject.getIntValue(CommonConstant.AjaxCode.CODE);
        if (code.equals(CommonConstant.AjaxCode.FAIL_CODE)) {
            return jsonObject;
        }

        jsonObject = new JSONObject();
        try {
            menuModel.setCreateDate(LocalDateTime.now());
            boolean flag = menuService.save(menuModel);
            if (flag) {
                jsonObject.put(CommonConstant.AjaxCode.CODE, CommonConstant.AjaxCode.SUCCESS_CODE);
                jsonObject.put(CommonConstant.AjaxCode.MESSAGE, "保存菜单数据成功！");
            } else {
                jsonObject.put(CommonConstant.AjaxCode.CODE, CommonConstant.AjaxCode.FAIL_CODE);
                jsonObject.put(CommonConstant.AjaxCode.MESSAGE, "保存菜单数据失败！");
            }
        } catch (Exception e) {
            log.error("保存菜单数据异常！原因：{}", e.getMessage());
            throw new JsonException(e.getMessage());
        }
        return jsonObject;
    }

    /**
     * 批量删除
     *
     * @param ids id集合
     */
    @ResponseBody
    @PostMapping("delete")
    @CacheLock(prefix = "menu-delete")
    @OperateLog(operateModule = "菜单管理", operateType = OperateConstant.DELETE, operateDesc = "删除菜单， 可以多个删除！")
    public JSONObject delete(String ids) {
        JSONObject jsonObject = new JSONObject();
        if (StringUtils.isBlank(ids)) {
            jsonObject.put(CommonConstant.AjaxCode.CODE, CommonConstant.AjaxCode.FAIL_CODE);
            jsonObject.put(CommonConstant.AjaxCode.MESSAGE, "请至少选择一条数据！");
            return jsonObject;
        }

        try {
            List<String> idList = Arrays.asList(ids.split(CommonConstant.DOC_FLAG));
            List<MenuModel> menuModels = menuService.listByIds(idList);
            if (menuModels != null && menuModels.size() > 0) {
                deleteChild(menuModels);
                jsonObject.put(CommonConstant.AjaxCode.CODE, CommonConstant.AjaxCode.SUCCESS_CODE);
                jsonObject.put(CommonConstant.AjaxCode.MESSAGE, "删除菜单数据成功！");
            } else {
                jsonObject.put(CommonConstant.AjaxCode.CODE, CommonConstant.AjaxCode.FAIL_CODE);
                jsonObject.put(CommonConstant.AjaxCode.MESSAGE, "请刷新数据重新选择要删除的数据！");
            }

            // menuService.removeByIds(idList);

        } catch (Exception e) {
            log.error("删除失败！原因： {}", e.getMessage());
            jsonObject.put(CommonConstant.AjaxCode.CODE, CommonConstant.AjaxCode.FAIL_CODE);
            jsonObject.put(CommonConstant.AjaxCode.MESSAGE, "删除菜单数据失败！");
        }

        return jsonObject;
    }

    private void deleteChild(List<MenuModel> menuModels) {
        for (MenuModel menuModel : menuModels) {
            menuService.removeById(menuModel.getId());
            List<MenuModel> menuList = menuService.getChildren(menuModel.getId());
            if (menuList != null && menuList.size() > 0) {
                deleteChild(menuList);
            }
        }

    }


    /**
     * 检测数据是否合格
     *
     * @param menuModel 角色数据对象
     * @param type      1： 修改 2：存储
     */
    protected JSONObject validate(MenuModel menuModel, int type) {
        JSONObject jsonObject = new JSONObject();
        jsonObject.put(CommonConstant.AjaxCode.CODE, CommonConstant.AjaxCode.FAIL_CODE);
        if (menuModel == null) {
            jsonObject.put(CommonConstant.AjaxCode.MESSAGE, "对象不能为空！");
            return jsonObject;
        }

        if (StringUtils.isBlank(menuModel.getName())) {
            jsonObject.put(CommonConstant.AjaxCode.MESSAGE, "请填写菜单名称！");
            return jsonObject;
        }

        // 判断角色名称是否合法
        if (!RegexUtil.matchUsername(menuModel.getName())) {
            jsonObject.put(CommonConstant.AjaxCode.MESSAGE, "菜单名称不合法！");
            return jsonObject;
        }

        if (StringUtils.isBlank(menuModel.getUrl())) {
            jsonObject.put(CommonConstant.AjaxCode.MESSAGE, "请填写菜单地址！");
            return jsonObject;
        }

        // 修改
        if (type == CommonConstant.DATA_UPDATE_TYPE) {
            if (StringUtils.isBlank(menuModel.getId())) {
                jsonObject.put(CommonConstant.AjaxCode.MESSAGE, "请至少选择一个对象数据！");
                return jsonObject;
            }
            // 判断菜单是否存在
            MenuModel menu = menuService.getById(menuModel.getId());
            if (menu == null) {
                jsonObject.put(CommonConstant.AjaxCode.MESSAGE, "该数据被删除，已经不存在！");
                return jsonObject;
            }
            // 判断菜单是否存在
            menu = menuService.getExistByFilter(menuModel);
            if (menu != null) {
                jsonObject.put(CommonConstant.AjaxCode.MESSAGE, "菜单已存在！");
                return jsonObject;
            }

        } else if (type == CommonConstant.DATA_SAVE_TYPE) {
            // 保存
            // 判断菜单是否存在
            MenuModel menu = menuService.getExistByFilter(menuModel);
            if (menu != null) {
                jsonObject.put(CommonConstant.AjaxCode.MESSAGE, "菜单名称或者菜单地址已存在！");
                return jsonObject;
            }

        } else {
            jsonObject.put(CommonConstant.AjaxCode.CODE, CommonConstant.AjaxCode.FAIL_CODE);
            jsonObject.put(CommonConstant.AjaxCode.MESSAGE, "数据类型错误！");
            return jsonObject;
        }
        jsonObject.put(CommonConstant.AjaxCode.CODE, CommonConstant.AjaxCode.SUCCESS_CODE);
        jsonObject.put(CommonConstant.AjaxCode.MESSAGE, "数据验证成功！");
        return jsonObject;
    }

    @GetMapping("/parentMenu")
    public String parentMenu() {
        return PAGE_PREFIX + "parentMenu";
    }

    @ResponseBody
    @GetMapping("/allList")
    public List<LayuiTreeNode> allList(@RequestParam(value = "roleId", required = false) String roleId) {
        List<MenuModel> list = menuService.getTopMenu();
        return fun(list, roleId);
    }

    private List<LayuiTreeNode> fun(List<MenuModel> list, String roleId) {
        if (list == null || list.size() <= 0) {
            return null;
        }

        List<String> urlList = null;
        if (StringUtils.isNotBlank(roleId) && roleService.getById(roleId) != null) {
            String code = roleService.getById(roleId).getCode();
            urlList = roleService.getUrlByRole(Collections.singletonList(code));
        }
        //  菜单转换成threeNode
        return menu2Node(list, urlList);
    }

    /**
     * 菜单转换成threeNode
     * @param menuList
     *      菜单列表
     * @param urlList
     *      角色拥有的url地址
     * @return 树形节点
     */
    private List<LayuiTreeNode> menu2Node(List<MenuModel> menuList, List<String> urlList) {
        List<LayuiTreeNode> nodeList = new ArrayList<>();
        LayuiTreeNode node;
        for (MenuModel menuModel : menuList) {
            node = new LayuiTreeNode();
            node.setTitle(menuModel.getName());
            node.setId(menuModel.getId());
            node.setField("id");
            List<MenuModel>  childrenList = menuService.getChildren(menuModel.getId());
            // 匹配这个角色的url，并且只有根节点上设置checked属性
            if (urlList != null && childListIsNull(childrenList)) {
                if (urlList.stream().anyMatch(url -> StringUtils.equals(menuModel.getUrl(), url))) {
                	node.setChecked(true);
                }
            }

            if (null != childrenList && childrenList.size() > 0) {
                node.setChildren(menu2Node(childrenList, urlList));
            }

            nodeList.add(node);
        }
        return nodeList;
    }

    /**
     * 判断列表为空
     *
     * @param childrenList
     *      菜单列表
     * @return true: 空列表 false：不是空列表
     */
    private boolean childListIsNull(List<MenuModel>  childrenList) {
        return null == childrenList || childrenList.size() <= 0;
    }
}
